package com.hmdp.service.impl;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hmdp.dto.Result;
import com.hmdp.entity.Shop;
import com.hmdp.entity.ShopExpire;
import com.hmdp.mapper.ShopMapper;
import com.hmdp.service.IShopService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.utils.CacheManager;
import io.netty.util.internal.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.sql.Time;
import java.time.LocalDateTime;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 虎哥
 * @since 2021-12-22
 */
@Slf4j
@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private CacheManager cacheManager;
    /**
     * 返回null防止缓存穿透
     * @param id
     * @return
     */
//    public Result getById(Long id) {
//        //读取缓存
//        String cacheShop = stringRedisTemplate.opsForValue().get("cache:shop:" + id);
//        //缓存命中返回
//        if (!StringUtil.isNullOrEmpty(cacheShop))
//            return Result.ok(JSONUtil.toBean(cacheShop, Shop.class));
//        //空值缓存，防止缓存击穿
//        if (Objects.equals(cacheShop, "")) return Result.fail("商铺信息不存在");
//        //缓存未命中，查询数据库
//        Shop shop = query().eq("id", id).one();
//        //数据库未找到，报错
//        if (shop == null) {
//            stringRedisTemplate.opsForValue().set("cache:shop:" + id, "");
//            stringRedisTemplate.expire("cache:shop:" + id,2, TimeUnit.MINUTES);
//            return Result.fail("商铺信息不存在");
//        }
//        //数据库找到，存入缓存
//        ObjectMapper objectMapper = new ObjectMapper();
//        String jsonShop = JSONUtil.toJsonStr(shop);
//        stringRedisTemplate.opsForValue().set("cache:shop:" + id, jsonShop);
//        stringRedisTemplate.expire("cache:shop:" + id, 30, TimeUnit.MINUTES);
//        //返回
//        return Result.ok(shop);
//    }

    /**
     * 互斥锁防止缓存击穿
     *
     * @param id
     * @return
     */
//    public Result getById(Long id) {
//
//        //查询缓存
//        String cacheShop = stringRedisTemplate.opsForValue().get("cache:shop:" + id);
//        //缓存命中
//        if (!StringUtil.isNullOrEmpty(cacheShop))
//            //返回
//            return Result.ok(JSONUtil.toBean(cacheShop, Shop.class));
//        Shop shop;
//        try {
//            //缓存不命中,访问锁
//            Boolean lock = stringRedisTemplate.opsForValue()
//                    .setIfAbsent("cache:shopLock" + id, "ok");
//            //无法访问锁
//            if (lock == false) {
//                //阻塞等待
//                Thread.sleep(50);
//                return getById(id);
//            }
//            //能够访问锁,读取数据库并回写缓存
//            try {
//                Thread.sleep(10000);
//            } catch (InterruptedException e) {
//                throw new RuntimeException(e);
//            }
//            shop = query().eq("id", id).one();
//            if (shop == null) return Result.fail("店铺信息不存在");
//            stringRedisTemplate.opsForValue().set("cache:shop:" + id, JSONUtil.toJsonStr(shop));
//        } catch (InterruptedException e) {
//            throw new RuntimeException(e);
//        } finally {
//            //释放锁
//            stringRedisTemplate.delete("cache:shopLock" + id);
//        }
//        return Result.ok(shop);
//    }

//    /**
//     * 缓存击穿——逻辑过期策略
//     *
//     * @param id
//     * @return
//     */
//    public Result getById(Long id) {
//        //访问锁
//        Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("cache:shopLock:" + id, "ok");
//        //查询缓存
//        String shopExpireStr = stringRedisTemplate.opsForValue().get("cache:shop:" + id);
//        log.info("shopExpireStr:{}", shopExpireStr);
//        if (Boolean.FALSE.equals(lock)) {
//            //无法访问锁，查询缓存并返回
//            //缓存为空，直接返回报错
//            if (shopExpireStr == null) return Result.fail("无店铺信息");
//            //不为空，返回缓存对应shop
//            ShopExpire shopExpire1 = JSONUtil.toBean(shopExpireStr, ShopExpire.class);
//            JSONObject shopData = (JSONObject) shopExpire1.getShopData();
//            Shop shop1 = JSONUtil.toBean(shopData, Shop.class);
//            return Result.ok(shop1);
//        }
//        //可以访问锁
//        //判断缓存是否为空，为空返回null并创建线程重写缓存
//        if (shopExpireStr == null) {
//            ExecutorService threadPool = Executors.newCachedThreadPool();
//            threadPool.execute(() -> {
//                saveShopExpire2Redis(id);
//                //释放锁
//                stringRedisTemplate.delete("cache:shopLock:" + id);
//            });
//            return Result.fail("无店铺信息");
//        }
//        //缓存不为空，判断是否过期
//        ShopExpire shopExpire1 = JSONUtil.toBean(shopExpireStr, ShopExpire.class);
//        JSONObject shopData = (JSONObject) shopExpire1.getShopData();
//        Shop shop1 = JSONUtil.toBean(shopData, Shop.class);
//        if (shopExpire1.getExpireTime().isAfter(LocalDateTime.now())) {
//            //没有过期，直接返回缓存内容
//            return Result.ok(shop1);
//        }
//        //过期，创建新线程，让新线程完成创建，返回旧的缓存内容
//        ExecutorService threadPool = Executors.newCachedThreadPool();
//        threadPool.execute(() -> {
//            //回写数据，完成后释放锁
//            saveShopExpire2Redis(id);
//            stringRedisTemplate.delete("cache:shopLock:" + id);
//        });
//        //返回旧的缓存内容
//        String shop2Str = stringRedisTemplate.opsForValue().get("cache:shop:" + id);
//        Shop shop2 = JSONUtil.toBean(shop2Str, Shop.class);
//        return Result.ok(shop2);
//    }
//
//    /**
//     * 重建缓存
//     *
//     * @param id
//     */
//    public Shop saveShopExpire2Redis(Long id) {
//        //数据库查询店铺信息
//        Shop shop = query().eq("id", id).one();
//        //构建ShopExpire类
//        ShopExpire shopExpire = new ShopExpire();
//        shopExpire.setExpireTime(LocalDateTime.now().plusMinutes(30));
//        shopExpire.setShopData(shop);
//        //将该类存入redis中
//        stringRedisTemplate.opsForValue().set("cache:shop:" + id, JSONUtil.toJsonStr(shopExpire));
//        return shop;
//    }
    public Result getById(Long id) {
        Shop shop = cacheManager.cacheBreakdownByLogicalExpire(id,
                "cache:shop:" ,
                "cache:shopLock:" ,
                Shop.class,
                (id2) -> query().eq("id", id2).one(),
                10L,
                TimeUnit.MINUTES);
        return Result.ok(shop==null?"店铺不存在":shop);
    }

    @Override
    public Result update(Shop shop) {
        boolean successOrNot = updateById(shop);
        log.info("更新：{}", successOrNot ? "success" : "fail");
        Boolean delete = stringRedisTemplate.delete("cache:shop:" + shop.getId());
        log.info("delete:{}", delete ? "success" : "fail");
        return Result.ok();
    }
}
